#pragma once
#include "../core/nodes/classnode.h"
#include "../core/nodes/list.h"
#include "field.h"
#include "method.h"
#include "operator.h"
#include "constructor.h"
#include "inheritancesemantic.h"
#include "parentdata.h"
#include "methodvisitor.h"
#include "../templates/hstringlist.h"
#include "../code/classcode.h"
#include "../code/constanttable.h"
#include "../code/jbfile.h"

class SemanticVisitorData;
class MethodSignature;

class FlatShortForm
{
 public:
	    std::map<std::string,Field>        *         m_fields; //!< Field list
		std::map<std::string,Method>       *         m_methods; //!< Method list
	    std::vector<Operator>              *         m_operators; //!< Operator list

		inline FlatShortForm( std::map<std::string,Field>   & fields,
			                  std::map<std::string,Method>  & methods,
							  std::vector<Operator>         & operators
			                )
		{
			m_fields=&fields;
			m_methods=&methods;
			m_operators=&operators;
		}
};

class MergeData;

class ClassSemantic
{
 private:
		ClassCode      m_codedclass;
	    /** Used constant table
		 */
		ConstantTable * m_table;
		/** File, where it should be saved
		 */
		JBFile         * m_file;  //Used file for bytecode
		/**
		    Removes any from interface
		 */
	     void removeAnyFromInterface();
		 /**
		    Marks class of root
		  */
		 bool m_isroot;
		 /**
		    Constructors
		  */
		 std::vector<Constructor*> m_constructors;
		 /** Defines a conversion from type Key - type, Value- method
		  */
		 std::map<std::string,std::string> m_converts_from;
		 /** Defines a conversion to type Key - type, Value - method 
		   */
		 std::map<std::string, std::string> m_converts_to;
		 /** Collects parents by wave
			 \param[out] waves waves used by index
			 \param[in]  index used index 
			 \param[in]  data  used data
		   */
		 void collectParents(std::vector<std::vector<std::string> > & waves, int index, SemanticVisitorData * data);
 public: 
		 ClassNode * m_class; //!< Class type node

		 std::map<std::string,Field>                 m_fields; //!< Field list
		 std::map<std::string,Method>                m_methods; //!< Method list
	     std::vector<Operator>                       m_operators; //!< Operator list
		 std::map<std::string,InheritanceSemantic>   m_inheritance; //!< Inheritance data

		 bool m_hierarchy_visited;
		 bool m_is_interface;  //Test, if class is semantic

		 void merge(SemanticVisitorData * data, 
				    const std::string & semanticName,
					InheritanceSemantic & redefren,
					FlatShortForm  & fsf
				   );
		 void mergeField(MergeData * data);
		 void mergeMethod(MergeData * data);
		 void mergeOperator(MergeData * data);
 public:
		/**
			Destructor
		 */
	    ~ClassSemantic();

	    inline void addMethod(const std::string & name, const Method & m)
	    {
			m_methods.insert(std::make_pair<std::string,Method>(name,m));
	    }
		inline void addOperator(const Operator & m)
	    {
			m_operators.push_back(m);
	    }

	    /*! Class node, that is being extracted
		 */
		ClassSemantic(ClassNode * cnode);

		inline ClassNode * cnode() { return m_class; }
		inline ClassSemantic() { m_class=NULL; m_hierarchy_visited=false; m_isroot=false; m_table=NULL;}
			
		inline unsigned int semanticInheritCount() {
			return m_inheritance.size();
		}

		inline const std::string & semanticInherit(unsigned int i) {
			std::map<std::string,InheritanceSemantic>::iterator it=m_inheritance.begin();
			for (unsigned int ik=0;ik<i;ik++)
				it++;
			return it->first;
		}
		/*! Used to perform inheritance check
		 */
		inline bool isEqualInheritance(unsigned int i,InheritanceSemantic * sem) {
			std::map<std::string,InheritanceSemantic>::iterator it=m_inheritance.begin();
			for (unsigned int ik=0;ik<i;ik++)
				it++;
			return &(it->second)==sem;
		}

		inline unsigned int inheritanceCount() 
		{ 
			if (!m_class->inherits()) return 0;
			return m_class->inherits()->vector().size(); 
		}
		inline ClassTypeNode * inherited(unsigned int i)
		{
			return (m_class->inherits()->vector()[i])->className();
		}
		inline InheritanceNode * inheritedNode(unsigned int i)
		{
			return (m_class->inherits()->vector()[i]);
		}

		void fillAttributes(SemanticVisitorData * data);

		void printAttrTable();

		inline void toggleVisited() { m_hierarchy_visited=true; }
		inline bool isHierarchyVisited() { return m_hierarchy_visited;}
	
		/*! Adds a new inheritance
			\param[in] ancestor ancestor string name
			\param[in] sem      inheritance
		 */
		void addInheritance(const std::string & ancestor, const  InheritanceSemantic & sem);
		/*! Fills an inheritance map with new inheritance instances
		 */
		void fillInheritance(SemanticVisitorData * data);
		/*! Generates flat shot form if needed
			\param[in] data needed data
		 */
		void generateFlatShotForm(SemanticVisitorData * data);
		/*! Get flat short form 
			\param[in] data needed data
		 */
		FlatShortForm  getFlatShotForm(SemanticVisitorData * data);

		bool getOperatorBySignature(const std::string & s,MethodSignature & o,  Method ** out);

		void checkInterface(SemanticVisitorData * data);
		inline void markAsInterface()  {m_is_interface=true;}
		inline bool isInterface() const { return m_is_interface; }

		void checkFMI(SemanticVisitorData * data);

		/*! Returns a method by name
		    \param[in] name name of array
			\return method
		 */
		Method * getMethodByName(const std::string & name);
		/*! Returns a method by name
		    \param[in] name name of array
			\return method
		 */
		Field * getFieldByName(const std::string & name);

		/*! Returns a constructor by method name
			\param[in] methname name of method
			\return constructor
		 */
		Constructor * getConstructorByMethodName(const std::string & name);
		/*! Finds all constructors in class
		 */
		void findConstructors(ParentData * parent);
		/*! Finds all constructors in class
		 */
		void fillConverters(ParentData * parent);
		/*! Adds a new constructor with following parameters Also fixes clients if needed
			\param[in] name string name
			\param[in] clients
			\return true on success.
		 */
		bool addConstructor(const std::string & name, const std::vector<std::string > & clients);
		/*! Marks a semantic as root
		 */
		inline void markAsRoot() { m_isroot=true; }
		/*! Converts from?
		 */
		inline bool convertsFrom(const std::string & from) { return m_converts_from.find(from)!=m_converts_from.end(); }
		/*! Converts from?
		 */
		inline bool convertsTo(const std::string & to) { return m_converts_to.find(to)!=m_converts_to.end(); }
		/*! Gets a conversion method from type
		 */
		inline const std::string getConversionMethodFrom(const std::string & from) { return m_converts_from[from]; }
		/*! Gets a conversion method to type
		 */
		inline const std::string getConversionMethodTo(const std::string & to)     { return m_converts_to[to]; }
		/*! Adds a conversion method from
		 */
		inline bool addConvertFrom(const std::string & type, const std::string & method)
		{
			if (convertsFrom(type))
				return false;
			m_converts_from.insert(std::make_pair<std::string,std::string>(type,method));
			return true;
		}
		/*! Adds a conversion method to
		 */
		inline bool addConvertTo(const std::string & type, const std::string & method)
		{
			if (convertsTo(type))
				return false;
			m_converts_to.insert(std::make_pair<std::string,std::string>(type,method));
			return true;
		}
		/*! Checks a local variables and formal arguments
		 */
		void checkFormalArgsAndLocalVars(ParentData * data);
		/*! Fixes all client access methods
		 */
		void fixClients();
		/*! Applies a visitor to semantic
		 */
		void applyVisitor(MethodVisitor * visitor);
		/*! Builds an hierarchy
			\param[in] hierarchy used hierarchy
			\param[in] data used data
		 */
		void buildHierarchy(std::vector<std::string> & hierarchy, SemanticVisitorData * data);
		/*! Tests, whether type can be converted without downcast wo other type
			\param[in] type  type to convert to
			\param[in] data  used data
		 */
		bool canConvertWithoutDowncast(const std::string & type, SemanticVisitorData * data);
		/*! Returns an array put operator
			\param[in] indexType used index
			\param[in] putType   pushed type
			\param[in] data      used data
		 */
		Method * getArrayPutOperator(ClassSemantic * indexType,ClassSemantic * putType,SemanticVisitorData * data);
		/*! Returns an operator with selected signature
			\param[in] name             name of operator
			\param[in] param            operatorParameter
		 	\param[in] data				used data
		 */
		Method * getBinaryOperator(const std::string & name, ClassSemantic * param,SemanticVisitorData * data);
		/*! Returns an operator with selected signature
			\param[in] name   name of operator
		 */
		Method * getUnaryOperator(const std::string & name);
		/*! Adds any if needed
		 */
		void addAnyIfNeed(SemanticVisitorData * data);

		/*! If semantic is array
		 */
		inline bool isArrayT()
		{
			hst::string str=cnode()->name().c_str();
			return str.getOccurence("ARRAY")!=0 && str.getOccurence("[")!=-1;
		}

		/*! If it is a root
		 */
		inline bool isRoot() const
		{
			return m_isroot;
		}

		/*! Returns a root method 
			\return root method
		 */
		inline Method * getRootMethod() {
			for (std::map<std::string,Method>::iterator it=m_methods.begin();it!=m_methods.end();it++)
				if (it->second.isRoot())
					return &( it->second );
			return NULL;
		}

		inline ClassCode & codedClass() {
			return m_codedclass;
		}
		inline ConstantTable * constantTable() {
			return m_table;
		}

		inline void initConstantTable() {
			m_table=new ConstantTable();
		}
 
		/*! Returns an amount of methods, that will be compiled
		 */
		unsigned short getCompiledMethodsCount();
		/*! Returns an amount of fileds, that will be compiled
		 */
		unsigned short getCompiledFieldsCount();

		/*! Sets a current output file
		 */
		inline void setFile(JBFile * file)
		{
			m_file=file;
		}
		/*! Returns output file
		 */
		inline JBFile * file() const {
			return m_file;
		}

		/*! Performs a checks for duplicate inheritance in the hierarchy
			\param[in] data used data
		 */
		void checkDuplicateInheritance(SemanticVisitorData * data);

		/*! Returns an ancestors of class, including himself
		 */
		std::vector<std::string> getAncestorsIncludingSelf(SemanticVisitorData * data);
};

/*! Returns a compatible java class name
    \param[in] name name of file
	\return java name
 */
inline std::string getJavaName(const std::string & name)
{
	hst::string nName(name.c_str());
	nName.replaceAllOccurences("[","<L");
	nName.replaceAllOccurences("]",";>");
	return nName.data();
}